/**
 * Copyright (c) 2021 Choice, Inc.
 * All Rights Reserved.
 * Choice Proprietary and Confidential.
 */
package cn.ruleengine.web.service.impl;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import javax.annotation.Resource;

import com.alibaba.fastjson.JSON;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;

import cn.ruleengine.core.DefaultInput;
import cn.ruleengine.core.Input;
import cn.ruleengine.core.Output;
import cn.ruleengine.core.RuleEngineConfiguration;
import cn.ruleengine.core.RuleSetEngine;
import cn.ruleengine.core.rule.RuleSet;
import cn.ruleengine.web.enums.DataStatus;
import cn.ruleengine.web.service.RuleEngineActionService;
import cn.ruleengine.web.store.entity.RuleEngineRuleSet;
import cn.ruleengine.web.store.entity.RuleEngineRuleSetPublish;
import cn.ruleengine.web.store.manager.RuleEngineRuleSetManager;
import cn.ruleengine.web.store.manager.RuleEngineRuleSetPublishManager;
import cn.ruleengine.web.vo.generalrule.ConsensusRequest;
import cn.ruleengine.web.vo.monitor.ConsensusResponse;
import lombok.extern.slf4j.Slf4j;

/**
 * 执行规则引擎
 *
 * @author <a href="mailto:danliang@myweimai.com">丹良</a>
 * @date 2021/7/28
 */
@Slf4j
@Service
public class RuleEngineActionServiceImpl implements RuleEngineActionService {

    @Resource
    private RuleEngineConfiguration         ruleEngineConfiguration;
    @Resource
    private RuleEngineRuleSetPublishManager ruleEngineRuleSetPublishManager;
    @Resource
    private RuleEngineRuleSetManager        ruleEngineRuleSetManager;

    @Override
    public List<ConsensusResponse> action(List<ConsensusRequest.It> requests) {
        Map<String, CompletableFuture<ConsensusResponse>> futuresMap = new HashMap<>(requests.size() * 2);
        for (ConsensusRequest.It request : requests) {
            futuresMap.put(
                    request.getShareCode(),
                    CompletableFuture.supplyAsync(() -> {
                        ConsensusResponse result = null;
                        try {
                            Long ms = System.currentTimeMillis();
                            result = doAction(request);
                            log.info("=====> doAction 时间:{}ms", System.currentTimeMillis() - ms);
                        } catch (Exception e) {
                            log.error("do action exception, request={}, exception detail", request, e);
                            result = ConsensusResponse.of(request.getShareCode(),null, null);
                        }
                        return result;
                    })
            );
        }

        List<ConsensusResponse> responses = new ArrayList<>();
        try {
            CompletableFuture.allOf(futuresMap.values().toArray(new CompletableFuture[requests.size()]))
                             .whenCompleteAsync((result, exception) -> {
                                if (exception != null) {
                                    log.error("InterruptedException, exception detail:{}", exception);
                                }
                             }).get();
        } catch (InterruptedException e) {
            log.error("InterruptedException, exception detail", e);
            return responses;
        } catch (ExecutionException e) {
            log.error("ExecutionException, exception detail", e);
            return responses;
        }

        futuresMap.forEach((k, v) -> {
            try {
                responses.add(v.get());
            } catch (InterruptedException e) {
                log.error("InterruptedException, k={}, v={}, exception detail", k, v, e);
            } catch (ExecutionException e) {
                log.error("ExecutionException, k={}, v={}, exception detail", k, v, e);
            }
        });
        return responses;
    }

    private ConsensusResponse doAction(ConsensusRequest.It request) {
        log.info("do action, request={}", request);
        String codePrue = request.getShareCode();
        RuleSet ruleSet;
        // 是否走内存
        if (true) {
            Long ruleSetId = ruleEngineConfiguration.getRuleEngineRuleSetCache().getKey(codePrue);
            if (Objects.isNull(ruleSetId)) {
                log.info("不存在规则集, ConsensusRequest.It[{}]", request);
                return ConsensusResponse.of(codePrue, null, null);
            }
            ruleSet = ruleEngineConfiguration.getRuleEngineRuleSetPublishCache().get(ruleSetId);
        } else {
            LambdaQueryWrapper<RuleEngineRuleSet> wrapper = new LambdaQueryWrapper();
            wrapper.like(RuleEngineRuleSet::getCode, codePrue)
                    .orderByDesc(RuleEngineRuleSet::getUpdateTime);
            List<RuleEngineRuleSet> ruleEngineRuleSets = this.ruleEngineRuleSetManager.list(wrapper);
            if (CollectionUtils.isEmpty(ruleEngineRuleSets)) {
                log.info("不存在规则集, ConsensusRequest.It[{}]", request);
                return ConsensusResponse.of(codePrue,null, null);
            }
            RuleEngineRuleSet ruleEngineRuleSet = ruleEngineRuleSets.get(0);
            String workspace = ruleEngineRuleSet.getWorkspaceCode();
            String code = ruleEngineRuleSet.getCode();
            RuleEngineRuleSetPublish ruleSetPublish = this.ruleEngineRuleSetPublishManager
                    .lambdaQuery()
                    .eq(RuleEngineRuleSetPublish::getStatus, DataStatus.PUBLISHED.getStatus())
                    .eq(RuleEngineRuleSetPublish::getRuleSetCode, code)
                    .eq(RuleEngineRuleSetPublish::getWorkspaceCode, workspace)
                    .one();
            ruleSet = RuleSet.buildRuleSet(ruleSetPublish.getData());

        }
        String workspace = ruleSet.getWorkspaceCode();
        String code = ruleSet.getCode();
        if (ruleSet == null) {
            log.info("不存在规则集, ConsensusRequest.It[{}]", request);
            return ConsensusResponse.of(codePrue, null, null);
        }

        Input input = new DefaultInput();
        List<ConsensusRequest.Diagnosis> diagnoses = request.getDiagnoses();
        if (CollectionUtils.isNotEmpty(diagnoses)) {
            for (ConsensusRequest.Diagnosis diagnosis : diagnoses) {
                input.put(diagnosis.getCode(), diagnosis.getType());
            }
        }
        input.put("gender", request.getGender());
        input.put("age", request.getAge());
        input.put("ageUnit", request.getAgeUnit());

        input.put("isMedicalCommunity", request.getIsMedicalCommunity());
        input.put("orgLevel", request.getOrgLevel());
        input.put("departmentGbCode", request.getDepartmentGbCode());
        input.put("reportTime", request.getReportTime());
        input.put("reportAbnormalIndicator", request.getReportAbnormalIndicator());
        input.put("reportType", request.getReportType());
        input.put("reportOrgLevel", request.getReportOrgLevel());
        Map<String, Object> param = request.getParam();
        if (CollectionUtils.isNotEmpty(param)) {
            input.putAll(param);
        }

        // 加载变量
        Long start = System.currentTimeMillis();
        RuleSetEngine engine = new RuleSetEngine(ruleEngineConfiguration);

        log.info("=====> 内存时间2:{}ms", System.currentTimeMillis() - start);
        Long ms = System.currentTimeMillis();
        Output output = engine.execute(input, ruleSet, workspace, code);
        log.info("=====> result:{}", JSON.toJSONString(output));
        log.info("=====> execute 时间:{}ms", System.currentTimeMillis() - ms);

        if ("java.util.Collections$SingletonList".equals(output.getClassType())) {
            List<String> list = (List<String>) output.getValue();
            if (CollectionUtils.isNotEmpty(list)) {
                // 补充最后一个记录
                return ConsensusResponse.of(
                        codePrue,
                        ConsensusResponse.Status.of(list.get(0)),
                        (String) input.get("lastConditionName"));
            }
        }
        return ConsensusResponse.of(codePrue,null, null);
    }
}
